/**
 * Organization:  SimVentions, Inc.
 * Creation Date: Jul 22, 2011
 */
package org.omg.tacsit.ui.geometry;

import javax.swing.text.DefaultFormatterFactory;
import javax.swing.text.InternationalFormatter;
import org.omg.tacsit.common.math.Angle;
import org.omg.tacsit.common.math.Distance;
import org.omg.tacsit.geometry.GeodeticPosition;
import org.omg.tacsit.geometry.DefaultGeodeticPosition;
import org.omg.tacsit.common.text.DegreeMinuteSecondFormat;
import org.omg.tacsit.common.ui.ComponentUtils;

/**
 * A panel which can display and edit a GeodeticPosition.
 * @author Matthew Child
 */
public class PositionPanel extends javax.swing.JPanel
{

  /**
   * The property that's fired when the Position is changed in the panel.
   */
  public static final String VALUE_PROPERTY = "value";
    
  private boolean editable;
  private GeodeticPosition value;
  
  private boolean isInitializingFieldValues;

  /**
   * Creates a new instance.
   */
  public PositionPanel()
  {
    editable = true;
    isInitializingFieldValues = false;
    initComponents();
  }

  /**
   * Gets the Position value currently displayed in the panel.
   * @return The position in the panel.
   */
  public GeodeticPosition getValue()
  {
    return value;
  }

  /**
   * Sets the Position value currently displayed in the panel.
   * @param value The position in the panel.
   */
  public void setValue(GeodeticPosition value)
  {
    GeodeticPosition oldValue = this.value;
    this.value = value;
    
    updateFieldValues(this.value);

    checkEditState();
    firePropertyChange(VALUE_PROPERTY, oldValue, this.value);
  }
  
  private void updateFieldValues(GeodeticPosition position)
  {
    isInitializingFieldValues = true;
    
    Angle latitude = Angle.ZERO;
    Angle longitude = Angle.ZERO;
    double altitudeMeters = 0.0d;

    if (position != null)
    {
      latitude = Angle.fromRadians(position.getLatitude());
      longitude = Angle.fromRadians(position.getLongitude());
      altitudeMeters = position.getAltitude();
    }
    
    latitudeField.setValue(latitude);
    longitudeField.setValue(longitude);
    altitudeField.setValue(altitudeMeters);
    
    isInitializingFieldValues = false;
  }

  /**
   * Sets the Position value currently being displayed in this panel.
   * @param latitude The latitude angle as an offset from the equator
   * @param longitude The longitude angle as an offset from the prime meridian.
   * @param altitudeMeters The distance from the surface in the earth (in meters)
   */
  public void setValue(Angle latitude, Angle longitude, double altitudeMeters)
  {
    GeodeticPosition newPosition = createPosition(latitude, longitude, altitudeMeters);
    setValue(newPosition);
  }

  /**
   * Creates a new position based on the 3 parts of a GeodeticPosition.  This allows subclasses to easily
   * override the type of position that's created.
   * @param latitude The angle latitude, measured as an offset from the equator.
   * @param longitude The angle longitude, measured as an offset from the prime meridian.
   * @param altitudeMeters The distance altitude (in meters).
   * @return A new position that has the same values as the passed parameters.
   */
  protected GeodeticPosition createPosition(Angle latitude, Angle longitude, double altitudeMeters)
  {
    return new DefaultGeodeticPosition(latitude, longitude, Distance.fromMeters(altitudeMeters));
  }

  private void checkEditState()
  {
    boolean propertiesEditable = isEditable();

    latitudeField.setEditable(propertiesEditable);
    longitudeField.setEditable(propertiesEditable);
    altitudeField.setEditable(propertiesEditable);
  }
  
  private void updateValueFromFields()
  {
    Angle latitude = (Angle)latitudeField.getValue();
    Angle longitude = (Angle)longitudeField.getValue();
    Double altitudeMeters = (Double)altitudeField.getValue();
    if(altitudeMeters == null)
    {
      altitudeMeters = 0.0;
    }
    setValue(latitude, longitude, altitudeMeters);
  }

  /**
   * Sets whether or not the position in the panel can be edited.
   * @param editable Whether or not the panel is able to be edited.
   */
  public void setEditable(boolean editable)
  {
    this.editable = editable;
    checkEditState();
  }

  /**
   * Gets whether or not the position in the panel can be edited.
   * @return True if the panel is editable, false otherwise.
   */
  public boolean isEditable()
  {
    return this.editable;
  }
    
  /** This method is called from within the constructor to
   * initialize the form.
   * WARNING: Do NOT modify this code. The content of this method is
   * always regenerated by the Form Editor.
   */
  @SuppressWarnings("unchecked")
  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
  private void initComponents() {

    javax.swing.JLabel latitudeLabel = new javax.swing.JLabel();
    javax.swing.JLabel longitudeLabel = new javax.swing.JLabel();
    latitudeField = new javax.swing.JFormattedTextField();
    longitudeField = new javax.swing.JFormattedTextField();
    javax.swing.JLabel altitudeLabel = new javax.swing.JLabel();
    altitudeField = new javax.swing.JFormattedTextField();
    javax.swing.JLabel altitudeUnitsLabel = new javax.swing.JLabel();

    latitudeLabel.setText("Latitude:");

    longitudeLabel.setText("Longitude:");

    latitudeField.setFormatterFactory(new DefaultFormatterFactory(new InternationalFormatter(new DegreeMinuteSecondFormat())));
    latitudeField.setHorizontalAlignment(javax.swing.JTextField.TRAILING);
    latitudeField.addPropertyChangeListener(new java.beans.PropertyChangeListener() {
      public void propertyChange(java.beans.PropertyChangeEvent evt) {
        latitudePropertyChanged(evt);
      }
    });

    longitudeField.setFormatterFactory(new DefaultFormatterFactory(new InternationalFormatter(new DegreeMinuteSecondFormat())));
    longitudeField.setHorizontalAlignment(javax.swing.JTextField.TRAILING);
    longitudeField.addPropertyChangeListener(new java.beans.PropertyChangeListener() {
      public void propertyChange(java.beans.PropertyChangeEvent evt) {
        longitudePropertyChanged(evt);
      }
    });

    altitudeLabel.setText("Altitude:");

    altitudeField.setFormatterFactory(new javax.swing.text.DefaultFormatterFactory(new javax.swing.text.NumberFormatter(new java.text.DecimalFormat("#,##0"))));
    altitudeField.setHorizontalAlignment(javax.swing.JTextField.TRAILING);
    altitudeField.addPropertyChangeListener(new java.beans.PropertyChangeListener() {
      public void propertyChange(java.beans.PropertyChangeEvent evt) {
        altitudePropertyChanged(evt);
      }
    });

    altitudeUnitsLabel.setText("Meters");

    javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
    this.setLayout(layout);
    layout.setHorizontalGroup(
      layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
      .addGroup(layout.createSequentialGroup()
        .addContainerGap()
        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
          .addComponent(longitudeLabel)
          .addComponent(altitudeLabel)
          .addComponent(latitudeLabel))
        .addGap(10, 10, 10)
        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
          .addComponent(altitudeField)
          .addComponent(latitudeField, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 100, Short.MAX_VALUE)
          .addComponent(longitudeField))
        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
        .addComponent(altitudeUnitsLabel)
        .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
    );
    layout.setVerticalGroup(
      layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
      .addGroup(layout.createSequentialGroup()
        .addContainerGap()
        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
          .addComponent(latitudeField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
          .addComponent(latitudeLabel))
        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
          .addComponent(longitudeField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
          .addComponent(longitudeLabel))
        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
          .addComponent(altitudeLabel)
          .addComponent(altitudeField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
          .addComponent(altitudeUnitsLabel))
        .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
    );
  }// </editor-fold>//GEN-END:initComponents

  private void latitudePropertyChanged(java.beans.PropertyChangeEvent evt)//GEN-FIRST:event_latitudePropertyChanged
  {//GEN-HEADEREND:event_latitudePropertyChanged
    if(!isInitializingFieldValues)
    {
      String propertyName = evt.getPropertyName();
      if (ComponentUtils.FORMATTED_FIELD_VALUE_PROPERTY.equals(propertyName))
      {
        updateValueFromFields();
      }
    }
  }//GEN-LAST:event_latitudePropertyChanged

  private void longitudePropertyChanged(java.beans.PropertyChangeEvent evt)//GEN-FIRST:event_longitudePropertyChanged
  {//GEN-HEADEREND:event_longitudePropertyChanged
    if(!isInitializingFieldValues)
    {
      String propertyName = evt.getPropertyName();
      if (ComponentUtils.FORMATTED_FIELD_VALUE_PROPERTY.equals(propertyName))
      {
        updateValueFromFields();
      }
    }
  }//GEN-LAST:event_longitudePropertyChanged

  private void altitudePropertyChanged(java.beans.PropertyChangeEvent evt)//GEN-FIRST:event_altitudePropertyChanged
  {//GEN-HEADEREND:event_altitudePropertyChanged
    if(!isInitializingFieldValues)
    {
      String propertyName = evt.getPropertyName();
      if (ComponentUtils.FORMATTED_FIELD_VALUE_PROPERTY.equals(propertyName))
      {
        updateValueFromFields();
      }
    }
  }//GEN-LAST:event_altitudePropertyChanged
  // Variables declaration - do not modify//GEN-BEGIN:variables
  private javax.swing.JFormattedTextField altitudeField;
  private javax.swing.JFormattedTextField latitudeField;
  private javax.swing.JFormattedTextField longitudeField;
  // End of variables declaration//GEN-END:variables
}
